package heap;

import lombok.SneakyThrows;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * https://www.bilibili.com/video/BV16E411n7Gk
 * StringTable 字符串常量池，不重复
 * 哈希表存储
 */
public class T06_StringTable {
    public static void main(String[] args) {
        // demo1();
        // demo2();
        // demo3();
        // demo4();
        demo5();
        demo6();
    }

    public static void demo1() {
        String s1 = "abc";
        String s2 = "abc";
        String s3 = new String(new char[]{'a', 'b', 'c'});
        String s4 = "ab" + "c"; // 将 "abc" 加入 StringTable
        String x = "a";
        String s5 = x + "bc";
        String s6 = s3.intern(); // 已有，返回 StringTable 中 "abc"

        System.out.println(s1 == s2); // true
        System.out.println(s1 == s3); // false
        System.out.println(s1 == s4); // true
        System.out.println(s1 == s5); // false
        System.out.println(s1 == s6); // true
    }

    public static void demo2() {
        String s1 = new String(new char[]{'x', 'y', 'z'});
        String s2 = s1.intern(); // jdk 1.7 及以后会把 xyz 加入字符串常量池。jdk 1.6 不会，即 s1 != s2
        String s3 = "xyz";
        System.out.println(s2 == s1); // true
        System.out.println(s2 == s3); // true
        System.out.println(s1 == s3); // true
    }

    @SneakyThrows
    public static void demo3() {
        List<String> address = new ArrayList<>(100);
        System.in.read();
        for (int i = 0; i < 99999; i++) {
            address.add(new String("xyz").intern()); // 减少内存占用
        }
    }

    public static void demo4() { // 字符串常量池位置，1.6 在方法区，1.8 在堆内存
        // 1.6: -XX:MaxPermSize=10M
        // 1.8: -Xmx10m -XX:-UseGCOverheadLimit 后一个参数是避免频繁 GC
        List<String> list = new ArrayList<>();
        while (true) {
            System.out.println(UUID.randomUUID().toString().intern());
            list.add(UUID.randomUUID().toString().intern()); // 添加到 list 保留引用，避免被 GC
        }
    }

    public static void demo5() { // 注意 JDK 版本
        String s1 = new String("a") + new String("b");
        s1.intern();
        String s2 = "ab";
        System.out.println(s1 == s2);
    }

    public static void demo6() {
        String s = new String("xxx"); // 会创建两个字符串对象(堆中一个，字符串常量池中一个)，都指向同一 char 数组 xxx

        String s2 = "ab";
        String s1 = new String("a") + new String("b");
        s1.intern(); // 常量池中已经有 ab 了，这里不会在放入
        System.out.println(s1 == s2);
    }

    public static void demo7() {
        String s1 = "abc";
        String s2 = "abc";
        String s3 = "c";
        String s4 = s2 + s3; // s2 s3 非 final 修饰，拼接底层会使用 StringBuilder，会生成新对象
        System.out.println(s1 == s4);
    }
}
